/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.homesoft.util;

import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 *
 * @author PhucAn
 */
public class DateTime implements Comparable<DateTime>, Serializable {

    Date date;
    Calendar cal;
    SimpleDateFormat sdf = new SimpleDateFormat();
    public static String DEFAULT_PATTERN = "dd/MM/yyyy";
    private static SimpleDateFormat defaultFormater = new SimpleDateFormat(DEFAULT_PATTERN);
    static String datePattern = "dd/MM/yyyy";
    static String datetimePattern = "dd/MM/yyyy-kk:mm:ss";
    private int type;
    public static final int DATE = 1;
    public static final int DATE_TIME = 2;
    public static final int JANUARY = 1;
    public static final int FEBRUARY = 2;
    public static final int MARCH = 3;
    public static final int APRIL = 4;
    public static final int MAY = 5;
    public static final int JUNE = 6;
    public static final int JULY = 7;
    public static final int AUGUST = 8;
    public static final int SEPTEMBER = 9;
    public static final int OCTOBER = 10;
    public static final int NOVEMBER = 11;
    public static final int DECEMBER = 12;
    public static final int SUNDAY = 1;
    public static final int MONDAY = 2;
    public static final int TUESDAY = 3;
    public static final int WEDNESDAY = 4;
    public static final int THURSDAY = 5;
    public static final int FRIDAY = 6;
    public static final int SATURDAY = 7;

    /**
     * Create new DateTime object with the current time
     */
    public DateTime() {
        date = new Date();
        init();
    }

    public DateTime(Date date) {
        this.date = date;
        init();
    }

    /**
     * Create new DateTime object with a specified Date object
     *
     * @param date
     */
    public DateTime(Date date, int type) {
        this.date = date;
        this.type = type;
        init();
    }

    public DateTime(long time, int type) {
        this.date = new Date(time);
        this.type = type;
        init();
    }

    /**
     * Create new DateTime object with specify date, time
     *
     * @param datetime string that represent for the date, time, which have
     * format: dd/MM/yyyy or dd/MM/yyyy-hh:mm:ss
     * @param type DateTime.DATE of DateTime.DATE_TIME
     */
    public DateTime(String datetime, int type) {
        this.type = type;
        date = parseToDate(datetime, type);
        init();
    }

    private void init() {
        cal = Calendar.getInstance();
        cal.setTime(date);
    }

    /**
     * **
     * Method that parseToDate the input string into DateTime
     *
     * @param datetime input string
     *
     * @param type integer value, DateTime.DATE or Datetime.DATE_TIME
     */
    public static Date parseToDate(String datetime, int type) {
        SimpleDateFormat sdf = new SimpleDateFormat();
        Date date = null;
        try {
            switch (type) {
                case DATE:
                    sdf.applyPattern(datePattern);
                    break;
                case DATE_TIME:
                    sdf.applyPattern(datetimePattern);
                    break;
                default:
                    sdf.applyPattern(datetimePattern);
                    break;
            }
            date = sdf.parse(datetime);
        } catch (ParseException e) {
            System.out.println("Error: " + e.getMessage());
        }
        return date;
    }

    public static DateTime parse(String datetime, int type) {
        SimpleDateFormat sdf = new SimpleDateFormat();
        DateTime date = null;
        try {
            switch (type) {
                case DATE:
                    sdf.applyPattern(datePattern);
                    break;
                case DATE_TIME:
                    sdf.applyPattern(datetimePattern);
                    break;
                default:
                    sdf.applyPattern(datetimePattern);
                    break;
            }
            date = new DateTime(sdf.parse(datetime), type);
        } catch (ParseException e) {
            System.out.println("Error: " + e.getMessage());
        } catch (Exception e) {
        }
        return date;
    }

    /**
     * Add a number of days from current date
     *
     * @param number an integer number
     * @return before date of following date
     */
    public Date addDays(int number) {
        Calendar c = Calendar.getInstance();
        c.setTime(new Date(date.getTime()));
        c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) + number);
        return getDateTime(c.getTime());
    }

    public Date addMonths() {
        throw new UnsupportedOperationException("This function is not yet supported");
    }

    /**
     * Return the current date as a Date objects
     *
     * @return
     */
    public Date getDateTime() {
        return getDateTime(date);
    }

    private Date getDateTime(Date d) {
        Calendar c = Calendar.getInstance();
        c.setTime(d);
        if (type == DATE) {
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);
            return c.getTime();
        } else {
            return d;
        }
    }

    /**
     * Return the current date as a string
     *
     * @return
     */
    public String getDateTimeAsString() {
        return date.toString();
    }

    /**
     * Get date time as string with specific pattern
     *
     * @param pattern pattern
     * @return string
     */
    public String getDateTimeAsString(String pattern) {
        String value = "null";
        try {
            sdf.applyPattern(pattern);
            value = sdf.format(date);
        } catch (Exception e) {
            System.out.println("Error: " + e.getMessage());
        }
        return value;
    }

    public static String getAsString(Date dateTime) {
        try {
            return defaultFormater.format(dateTime);
        } catch (Exception ex) {
            return "undefined";
        }
    }

    /**
     * Static method that calculates the difference day between two point of
     * time
     *
     * @param beforeDate the first day
     * @param affterDate the after day, it should be the greater one
     * @return difference days
     */
    public static int dayDiff(Date beforeDate, Date affterDate) {
        long diff = affterDate.getTime() - beforeDate.getTime();
        return (int) (diff / 1000 / 3600 / 24);
    }

    /**
     * Static method that returns the current date and time
     *
     * @return a DateTime object which represent for current time
     */
    public static DateTime getToday() {
        return new DateTime();
    }

    /**
     * Return year value
     *
     * @return
     */
    public int getYear() {
        return cal.get(Calendar.YEAR);
    }

    /**
     * Return month value
     *
     * @return int
     */
    public int getMonth() {
        return cal.get(Calendar.MONTH)+1;
    }

    /**
     * Return day value
     *
     * @return int
     */
    public int getDay() {
        return cal.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * Get hour value of current DateTime object
     *
     * @return int
     */
    public int getHour() {
        return cal.get(Calendar.HOUR);
    }

    /**
     * Get hour in day (24 hours)
     *
     * @return
     */
    public int getHourOfDay() {
        return cal.get(Calendar.HOUR_OF_DAY);
    }

    /**
     * Get minute value of current DateTime object
     *
     * @return int
     */
    public int getMinute() {
        return cal.get(Calendar.MINUTE);
    }

    /**
     * Get second value of current DateTime object
     *
     * @return int
     */
    public int getSecond() {
        return cal.get(Calendar.SECOND);
    }

    /**
     * Get day of year
     *
     * @return int - the first day has value 1
     */
    public int getDayOfYear() {
        return cal.get(Calendar.DAY_OF_YEAR);
    }

    /**
     * Get day of month
     *
     * @return int - the first day of month has value 1
     */
    public int getDayOfMonth() {
        return cal.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * Get day of week
     *
     * @return int - the first day is Sunday, which has value 1
     */
    public int getDayOfWeek() {
        return cal.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * Set value for the current DateTime object
     *
     * @param datetime string that represent for the date
     * @param type int value, DATE or DATE_TIME
     */
    public void setDateTime(String datetime, int type) {
        date = parseToDate(datetime, type);
    }

    /**
     * Set value for the current DateTime object
     *
     * @param date a Date object
     */
    public void setDateTime(Date date) {
        this.date = date;
    }

    /**
     * Add to year value
     *
     * @param value integer value, that is number of years to add
     * @return DateTime object
     */
    public DateTime addYear(int value) {
        DateTime dt = this.clone();
        dt.cal.add(Calendar.YEAR, value);
        dt.date = dt.cal.getTime();
        return dt;
    }

    /**
     * Add to month value
     *
     * @param value number of months to add
     * @return DateTime object
     */
    public DateTime addMonth(int value) {
        DateTime d = this.clone();
        d.cal.add(Calendar.MONTH, value);
        d.date = d.cal.getTime();
        return d;
    }

    /**
     * Add to day value
     *
     * @param value number of days to add
     * @return DateTime object
     */
    public DateTime addDay(int value) {
        DateTime dt = this.clone();
        dt.cal.add(Calendar.DAY_OF_MONTH, value);
        dt.date = dt.cal.getTime();
        return dt;
    }

    /**
     * Add to hour value
     *
     * @param value number of hours to add
     * @return DateTime object
     */
    public DateTime addHour(int value) {
        DateTime dt = this.clone();
        dt.cal.add(Calendar.HOUR, value);
        dt.date = dt.cal.getTime();
        return dt;
    }

    /**
     * Add to minute value
     *
     * @param value number of minutes to add
     * @return DateTime object
     */
    public DateTime addMinute(int value) {
        DateTime dt = this.clone();
        dt.cal.add(Calendar.MINUTE, value);
        dt.date = dt.cal.getTime();
        return dt;
    }

    /**
     * Add to second value
     *
     * @param value number of second to add
     * @return DateTime object
     */
    public DateTime addSecond(int value) {
        DateTime dt = this.clone();
        dt.cal.add(Calendar.SECOND, value);
        dt.date = dt.cal.getTime();
        return dt;
    }

    @Override
    public DateTime clone() {
        DateTime datetime = new DateTime();
        datetime.setDateTime(this.date);
        datetime.init();
        return datetime;
    }

    /**
     * Compare two point of time
     *
     * @param obj the date to compare with
     * @return -1 if the current point of time is before the value of obj 0 if
     * two value are equal 1 if the current point of time is the greater one
     */
    @Override
    public int compareTo(DateTime obj) {
        if (this.date.compareTo(obj.date) > 0) {
            return 1;
        } else if (this.date.compareTo(obj.date) == 0) {
            return 1;
        } else {
            return -1;
        }
    }

    /**
     * Check whether the current DateTime object exactly equals to specified
     * object
     *
     * @param date the specified DateTime object
     * @return true if they are exactly equal, else return false
     */
    public boolean equals(DateTime date) {
        return this.date.equals(date.date);
    }

    /**
     * Check whether the current date is equal to specified date. This mean that
     * they have the same year, month, and day.
     *
     * @param date
     * @return
     */
    public boolean equalsDate(DateTime date) {
        return this.getYear() == date.getYear() && this.getMonth() == date.getMonth()
                && this.getDay() == date.getDay();
    }

    /**
     * Check whether the current date is today
     *
     * @param date the date value to check
     * @return true if specified date have the same year, month and day with
     * today
     */
    public boolean isToday(DateTime date) {
        DateTime today = DateTime.getToday();
        return date.getYear() == today.getYear() && date.getMonth() == today.getMonth()
                && date.getDay() == today.getDay();
    }
}
